package com.demo13;


import com.github.unidbg.Emulator;
import com.github.unidbg.arm.backend.Backend;
import com.github.unidbg.arm.context.EditableArm32RegisterContext;
import com.github.unidbg.linux.ARM32SyscallHandler;
import com.github.unidbg.linux.file.ByteArrayFileIO;
import com.github.unidbg.linux.file.DumpFileIO;
import com.github.unidbg.memory.SvcMemory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.sun.jna.Pointer;
import unicorn.ArmConst;

import java.util.concurrent.ThreadLocalRandom;

class dewuSyscallHandler extends ARM32SyscallHandler {

    public dewuSyscallHandler(SvcMemory svcMemory) {
        super(svcMemory);
    }

    @Override
    protected boolean handleUnknownSyscall(Emulator emulator, int NR) {
        switch (NR) {
            case 190:
                vfork(emulator);
                return true;
            case 114:
                wait4(emulator);
                return true;

        }
        return super.handleUnknownSyscall(emulator, NR);
    }


    private void wait4(Emulator emulator) {
        return;
    }


    private void vfork(Emulator<?> emulator) {
        EditableArm32RegisterContext context = emulator.getContext();
        int childPid = emulator.getPid() +
                ThreadLocalRandom.current().nextInt(256);
        int r0 = childPid;
        System.out.println("vfork pid=" + r0);
        context.setR0(r0);
    }

    private static final int CLOCK_REALTIME = 0;
    private static final int CLOCK_MONOTONIC = 1;
    private static final int CLOCK_MONOTONIC_RAW = 4;
    private static final int CLOCK_MONOTONIC_COARSE = 6;
    private static final int CLOCK_BOOTTIME = 7;
    private final long nanoTime = System.nanoTime();
    @Override
    protected int clock_gettime(Backend backend, Emulator<?> emulator) {
        int clk_id = backend.reg_read(ArmConst.UC_ARM_REG_R0).intValue();
        Pointer tp = UnidbgPointer.register(emulator, ArmConst.UC_ARM_REG_R1);
        long offset = clk_id == CLOCK_REALTIME ? System.currentTimeMillis() * 1000000L : System.nanoTime() - nanoTime;
        long tv_sec = offset / 1000000000L;
        long tv_nsec = offset % 1000000000L;

        switch (clk_id) {
            case CLOCK_REALTIME:
            case CLOCK_MONOTONIC:
            case CLOCK_MONOTONIC_RAW:
            case CLOCK_MONOTONIC_COARSE:
            case CLOCK_BOOTTIME:
            case 2:
                tp.setInt(0, (int) tv_sec);
                tp.setInt(4, (int) tv_nsec);
                return 0;
        }
        throw new UnsupportedOperationException("clk_id=" + clk_id);
    }

    // 处理popen
    protected int pipe2(Emulator<?> emulator) {
        EditableArm32RegisterContext context = (EditableArm32RegisterContext)
                emulator.getContext();
        Pointer pipefd = context.getPointerArg(0);
        int flags = context.getIntArg(1);
        int write = getMinFd();
        this.fdMap.put(write, new DumpFileIO(write));
        int read = getMinFd();
        String command = emulator.get("command");
        System.out.println("fuck cmd:"+command);
        // stdout中写入popen command 应该返回的结果
        String stdout = "\n";
        // 如下三条，检测MAGISK
        if(command.equals("stat /data")){
            stdout = "  File: /data\n" +
                    "  Size: 4096     Blocks: 16      IO Blocks: 512 directory\n" +
                    "Device: 10305h/66309d    Inode: 2        Links: 53\n" +
                    "Access: (0771/drwxrwx--x)       Uid: ( 1000/  system)   Gid: ( 1000/  system)\n" +
                    "Access: 2022-04-22 16:08:42.656423789 +0800\n" +
                    "Modify: 1970-02-05 00:02:38.459999996 +0800\n" +
                    "Change: 1971-07-05 09:54:50.369999990 +0800";
        }
        if(command.equals("stat /cache")){
            stdout = "  File: /cache\n" +
                    "  Size: 4096     Blocks: 16      IO Blocks: 512 directory\n" +
                    "Device: 10304h/66308d    Inode: 2        Links: 7\n" +
                    "Access: (0770/drwxrwx---)       Uid: ( 1000/  system)   Gid: ( 2001/   cache)\n" +
                    "Access: 1970-01-02 11:44:27.000000000 +0800\n" +
                    "Modify: 2022-05-17 23:13:06.000000000 +0800\n" +
                    "Change: 2022-05-17 23:13:06.000000000 +0800";
        }
        if(command.equals("cd /sys/class/net/wlan0/ && cat address")){
            stdout = "12:34";
        }
        if(command.equals("cd /system/bin && ls -i")){
            stdout = " 682 abb                     797 fsync                  909 mkfs.ext3         1004 sha1sum";
        }
//        if(command.equals("cd  /proc/sys/kernel/ && stat random")){
//            stdout = "nnnn";
//        }
        if(command.equals("stat /root")){
            stdout = "";
        }
        if(command.equals("stat /proc/sys/kernel/random")){
            stdout = "  File: /proc/sys/kernel/random\n" +
                    "  Size: 0        Blocks: 0       IO Blocks: 512 directory\n" +
                    "Device: 4h/4d    Inode: 70790    Links: 1\n" +
                    "Access: (0555/dr-xr-xr-x)       Uid: (    0/    root)   Gid: (    0/    root)\n" +
                    "Access: 2022-05-17 23:14:00.493999978 +0800\n" +
                    "Modify: 2022-05-17 23:14:00.493999978 +0800\n" +
                    "Change: 2022-05-17 23:14:00.493999978 +0800";
        }
        if(command.equals("stat /system/etc/firmware")){
            stdout = "";
        }
        if(command.equals("uname -a")){
            stdout = "Linux localhost 4.9.186-perf-gd3d6708 #1 SMP PREEMPT Wed Nov 4 01:05:59 CST 2020 aarch64";
        }
        if(command.equals("uname -r")){
            stdout = "4.9.186-perf-gd3d6708";
        }
        if(command.equals("/system/bin && ls -l")){
            stdout = "-rwxr-xr-x 1 root shell 128688 2009-01-01 08:00 abb\n" +
                    "lrwxr-xr-x 1 root shell      6 2009-01-01 08:00 acpi -> toybox\n" +
                    "-rwxr-xr-x 1 root shell    207 2009-01-01 08:00 am\n" +
                    "lrwxr-xr-x 1 root shell     13 2009-01-01 08:00 app_process -> app_process64";
        }
        if(command.equals("cd  /proc/sys/kernel/ && stat random")){
            stdout = "  File: random\n" +
                    "  Size: 0        Blocks: 0       IO Blocks: 512 directory\n" +
                    "Device: 4h/4d    Inode: 70790    Links: 1\n" +
                    "Access: (0555/dr-xr-xr-x)       Uid: (    0/    root)   Gid: (    0/    root)\n" +
                    "Access: 2022-05-17 23:14:00.493999978 +0800\n" +
                    "Modify: 2022-05-17 23:14:00.493999978 +0800\n" +
                    "Change: 2022-05-17 23:14:00.493999978 +0800";
        }
        /*
        Mandatory arguments to long options are mandatory for short options too.
          -L, --dereference     follow links
          -f, --file-system     display file system status instead of file status
              --cached=MODE     specify how to use cached attributes;
                                  useful on remote file systems. See MODE below
          -c  --format=FORMAT   use the specified FORMAT instead of the default;
                                  output a newline after each use of FORMAT
              --printf=FORMAT   like --format, but interpret backslash escapes,
                                  and do not output a mandatory trailing newline;
                                  if you want a newline, include \n in FORMAT
          -t, --terse           print the information in terse form
              --append-exe      append .exe if cygwin magic was needed
              --help     display this help and exit
              --version  output version information and exit

        The --cached MODE argument can be; always, never, or default.
        `always` will use cached attributes if available, while
        `never` will try to synchronize with the latest attributes, and
        `default` will leave it up to the underlying file system.

        The valid format sequences for files (without --file-system):

          %a   permission bits in octal (note '#' and '0' printf flags)
          %A   permission bits and file type in human readable form
          %b   number of blocks allocated (see %B)
          %B   the size in bytes of each block reported by %b
          %C   SELinux security context string
          %d   device number in decimal
          %D   device number in hex
          %f   raw mode in hex
          %F   file type
          %g   group ID of owner
          %G   group name of owner
          %h   number of hard links
          %i   inode number
          %m   mount point
          %n   file name
          %N   quoted file name with dereference if symbolic link
          %o   optimal I/O transfer size hint
          %s   total size, in bytes
          %t   major device type in hex, for character/block device special files
          %T   minor device type in hex, for character/block device special files
          %u   user ID of owner
          %U   user name of owner
          %w   time of file birth, human-readable; - if unknown
          %W   time of file birth, seconds since Epoch; 0 if unknown
          %x   time of last access, human-readable
          %X   time of last access, seconds since Epoch
          %y   time of last data modification, human-readable
          %Y   time of last data modification, seconds since Epoch
          %z   time of last status change, human-readable
          %Z   time of last status change, seconds since Epoch

        Valid format sequences for file systems:

          %a   free blocks available to non-superuser
          %b   total data blocks in file system
          %c   total file nodes in file system
          %d   free file nodes in file system
          %f   free blocks in file system
          %i   file system ID in hex
          %l   maximum length of filenames
          %n   file name
          %s   block size (for faster transfers)
          %S   fundamental block size (for block counts)
          %t   file system type in hex
          %T   file system type in human readable form

         */
        if(command.equals("stat -c %i /system/framework")){
            stdout = "1958";
        }
        if(command.equals("stat -c %i /system/framework/am.jar")){
            stdout = "1949";
        }
        if(command.equals("stat -c %i /system/build.prop")){
            stdout = "1102";
        }
        if(command.equals("stat -c %i /system/fonts")){
            stdout = "1683";
        }
        if(command.equals("stat -c %i /system/app")){
            stdout = "362";
        }
        if(command.equals("stat -c %i /system")){
            stdout = "72";
        }
        if(command.equals("stat -c %d /data")){
            stdout = "66309";
        }
        if(command.equals("stat -c %d /system")){
            stdout = "64768";
        }
        if(command.equals("stat -f -c %i /data")){
            stdout = "efcd0a34e8e618f8";
        }
        if(command.equals("stat -c %X /storage/emulated/")){
            stdout = "1650614946";
        }
        if(command.equals("stat -c %X /data")){
            stdout = "1650614922";
        }
        this.fdMap.put(read, new ByteArrayFileIO(0, "pipe2_read_side", stdout.getBytes()));
        pipefd.setInt(0, read);
        pipefd.setInt(4, write);
        System.out.println("pipe2 pipefd=" + pipefd + ", flags=0x" + flags + ", read=" + read + ", write=" + write + ", stdout=" + stdout);
        context.setR0(0);
        return 0;
    }


}

